/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.domain.vo;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.iwindplus.boot.domain.constant.CommonConstant;
import com.iwindplus.boot.domain.constant.CommonConstant.ExceptionConstant;
import com.iwindplus.boot.domain.constant.CommonConstant.SymbolConstant;
import com.iwindplus.boot.domain.exception.BizException;
import com.iwindplus.boot.domain.exception.CommonException;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.Locale;
import java.util.Objects;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;

/**
 * 结果视图对象.
 *
 * @param <T> 泛型
 * @author zengdegui
 * @since 2020/10/31
 */
@Slf4j
@Schema(description = "结果视图对象")
@Getter
@ToString
public class ResultVO<T> implements Serializable {

    /**
     * 业务状态码.
     */
    @Schema(description = "业务状态码")
    @JsonProperty(CommonConstant.ResponseConstant.BIZ_STATUS)
    private Integer bizStatus;

    /**
     * 业务编码.
     */
    @Schema(description = "业务编码")
    @JsonProperty(CommonConstant.ResponseConstant.BIZ_CODE)
    private String bizCode;

    /**
     * 业务信息.
     */
    @Schema(description = "业务信息")
    @JsonProperty(CommonConstant.ResponseConstant.BIZ_MESSAGE)
    private String bizMessage;

    /**
     * 业务信息参数.
     */
    @Schema(description = "业务信息参数")
    @JsonProperty(CommonConstant.ResponseConstant.BIZ_MESSAGE_PARAMS)
    private Object[] bizMessageParams;

    /**
     * 业务数据.
     */
    @Schema(description = "业务数据")
    @JsonProperty(CommonConstant.ResponseConstant.BIZ_DATA)
    private T bizData;

    /**
     * 业务时间.
     */
    @Schema(description = "业务时间")
    @JsonProperty(CommonConstant.ResponseConstant.BIZ_TIME)
    private LocalDateTime bizTime;

    /**
     * 业务跟踪唯一标识.
     */
    @Schema(description = "业务跟踪唯一标识")
    @JsonProperty(CommonConstant.ResponseConstant.BIZ_TRACE_ID)
    private String bizTraceId;

    /**
     * 构造方法.
     */
    public ResultVO() {
        this.bizTime = LocalDateTime.now();
        this.bizTraceId = MDC.get(CommonConstant.TraceConstant.TRACE_ID);
    }

    /**
     * 成功.
     *
     * @param <T> 泛型
     * @return ResultVO <T>
     */
    public static <T> ResultVO<T> success() {
        return ResultVO.success(null);
    }

    /**
     * 成功.
     *
     * @param bizData 业务数据
     * @param <T>     泛型
     * @return ResultVO <T>
     */
    public static <T> ResultVO<T> success(T bizData) {
        HttpStatus httpStatus = HttpStatus.OK;
        return ResultVO.buildResult(httpStatus.value(), httpStatus.name().toLowerCase(), httpStatus.getReasonPhrase(), null, bizData);
    }

    /**
     * 失败.
     *
     * @param <T> 泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> error() {
        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        return ResultVO.error(httpStatus);
    }

    /**
     * 失败.
     *
     * @param httpStatus http状态码
     * @param <T>        泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> error(HttpStatus httpStatus) {
        return ResultVO.buildResult(httpStatus.value(), httpStatus.name().toLowerCase(), httpStatus.getReasonPhrase(), null);
    }

    /**
     * 失败.
     *
     * @param commonException 异常
     * @param <T>             泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> error(CommonException commonException) {
        return ResultVO.buildResult(commonException.getBizStatus(), commonException.getBizCode(),
            commonException.getBizMessage(), commonException.getBizMessageParams());
    }

    /**
     * 失败.
     *
     * @param commonException  异常
     * @param bizMessageParams 业务信息参数
     * @param <T>              泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> error(CommonException commonException, Object[] bizMessageParams) {
        bizMessageParams = ArrayUtil.isNotEmpty(bizMessageParams) ? bizMessageParams : commonException.getBizMessageParams();
        return ResultVO.buildResult(commonException.getBizStatus(), commonException.getBizCode(), commonException.getBizMessage(), bizMessageParams);
    }

    /**
     * 构造响应结果（消息原样输出）.
     *
     * @param bizStatus  业务状态码
     * @param bizCode    业务编码
     * @param bizMessage 业务信息
     * @param <T>        泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> buildSourceResult(Integer bizStatus, String bizCode, String bizMessage) {
        final ResultVO<T> result = new ResultVO<>();
        result.bizStatus = bizStatus;
        result.bizCode = bizCode;
        result.bizMessage = bizMessage;
        return result;
    }

    /**
     * 构造响应结果（消息国际化）.
     *
     * @param bizStatus  业务状态码
     * @param bizCode    业务编码
     * @param bizMessage 业务信息
     * @param <T>        泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> buildResult(Integer bizStatus, String bizCode, String bizMessage) {
        return ResultVO.buildResult(bizStatus, bizCode, bizMessage, null);
    }

    /**
     * 构造响应结果（消息国际化）.
     *
     * @param bizStatus        业务状态码
     * @param bizCode          业务编码
     * @param bizMessage       业务信息
     * @param bizMessageParams 业务信息参数
     * @param <T>              泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> buildResult(Integer bizStatus, String bizCode, String bizMessage, Object[] bizMessageParams) {
        return ResultVO.buildResult(bizStatus, bizCode, bizMessage, bizMessageParams, null);
    }

    /**
     * 构造响应结果（消息国际化）.
     *
     * @param bizStatus        业务状态码
     * @param bizCode          业务编码
     * @param bizMessage       业务信息
     * @param bizMessageParams 业务信息参数
     * @param bizData          业务数据
     * @param <T>              泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> buildResult(Integer bizStatus, String bizCode, String bizMessage, Object[] bizMessageParams, T bizData) {
        return ResultVO.buildResult(bizStatus, bizCode, bizMessage, bizMessageParams, bizData, null);
    }

    /**
     * 构造响应结果（消息国际化）.
     *
     * @param bizStatus        业务状态码
     * @param bizCode          业务编码
     * @param bizMessage       业务信息
     * @param bizMessageParams 业务信息参数
     * @param bizData          业务数据
     * @param locale           语言
     * @param <T>              泛型
     * @return ResultVO<T>
     */
    public static <T> ResultVO<T> buildResult(Integer bizStatus, String bizCode, String bizMessage,
        Object[] bizMessageParams, T bizData, Locale locale) {
        final ResultVO<T> result = new ResultVO<>();
        result.bizStatus = bizStatus;
        result.bizCode = bizCode;
        result.bizMessage = ResultVO.message(bizCode, bizMessageParams, bizMessage, locale);
        result.bizData = bizData;
        return result;
    }

    /**
     * 错误异常抛出.
     */
    public void errorThrow() {
        // 业务成功不抛出异常
        if (HttpStatus.OK.value() == this.getBizStatus()
            || HttpStatus.OK.name().toLowerCase().equals(this.getBizCode())) {
            return;
        }
        throw new BizException(this.bizStatus, this.bizCode, this.bizMessage, bizMessageParams);
    }

    /**
     * 根据消息键和参数获取国际化消息.
     *
     * @param code    编码
     * @param args    参数
     * @param message 消息
     * @param locale  语言
     * @return String
     */
    public static String message(String code, Object[] args, String message, Locale locale) {
        locale = ResultVO.getLocale(locale);
        MessageSource messageSource = ResultVO.getMessageSource();
        if (null == messageSource) {
            return ResultVO.replaceStr(message, args);
        }
        return messageSource.getMessage(code, args, message, locale);
    }

    /**
     * 替换字符串中变量.
     *
     * @param message 消息（变量示例："{0},{1}"）
     * @param args    参数
     * @return String
     */
    public static String replaceStr(String message, Object[] args) {
        if (CharSequenceUtil.isBlank(message) || ArrayUtil.isEmpty(args)) {
            return null;
        }
        try {
            return MessageFormat.format(message, args);
        } catch (Exception ex) {
            log.error(ExceptionConstant.EXCEPTION, ex);
        }
        return null;
    }

    /**
     * 获取国际化语言.
     *
     * @param locale 语言
     * @return Locale
     */
    public static Locale getLocale(Locale locale) {
        if (Objects.nonNull(locale)) {
            return locale;
        }
        String language = MDC.get(HttpHeaders.ACCEPT_LANGUAGE);
        if (CharSequenceUtil.isNotBlank(language) && CharSequenceUtil.contains(language, SymbolConstant.HORIZONTAL_LINE)) {
            locale = Locale.forLanguageTag(language);
        } else {
            locale = Locale.getDefault();
        }
        return locale;
    }

    /**
     * 获取MessageSource.
     *
     * @return MessageSource
     */
    public static MessageSource getMessageSource() {
        try {
            return SpringUtil.getBean(MessageSource.class);
        } catch (Exception ex) {
            log.error(ExceptionConstant.EXCEPTION, ex);
        }
        return null;
    }
}
